/*
 * 测试是否使用 modern opengl 绘制，测试使用 vertex array object 时，是否使用 buffer object 。
 * 目的是有时候调试代码时，会写一些临时代码，发现临时代码没有绘制出效果时还以为绘制的数据不对，最后发现和以上是有关系的。 
 */
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>

#define USE_OPENGL33
#define VAO_WITH_BO

const GLchar *vertexcode = 
"#version 330 core \n"
"layout(location = 0) in vec3 pos_modelspace; \n"
"void main() { \n"
"	gl_Position = vec4(pos_modelspace, 1); \n"
"}";

const GLchar *fragmentcode = 
"#version 330 core \n"
"out vec3 color; \n"
"void main() { \n"
"	color = vec3(1, 0, 0); \n"
"}";

static GLuint 
load_shader(GLenum shadertype, const GLchar *shadercode) {
	GLuint shader = glCreateShader(shadertype);
	if (shader == 0) {
		printf("fail to create shader\n");
		return 0;
	}

	// 编译
	glShaderSource(shader, 1, &shadercode, NULL);
	glCompileShader(shader);

	// 检查编译结果
	GLint value = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
	if (value == GL_FALSE) {
		value = 0;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &value);
		if (value > 1) {
			GLchar info[value + 1];
			glGetShaderInfoLog(shader, value, NULL, info);
			printf("fail to compile shader: %s\n", info);
		}
		glDeleteShader(shader);
		shader = 0;
	}

	return shader;
}

static GLuint 
link_program(const GLchar *vertexcode, const GLchar *fragmentcode) {
	GLuint vertexshader = 0, fragmentshader = 0, program = 0;
	vertexshader = load_shader(GL_VERTEX_SHADER, vertexcode);
	if (vertexshader == 0) {
		printf("fail to create vertex shader");
		goto EXIT;
	}
	fragmentshader  = load_shader(GL_FRAGMENT_SHADER, fragmentcode);
	if (fragmentshader == 0) {
		printf("fail to create fragment shader");
		goto EXIT;
	}

	program = glCreateProgram();
	if (program == 0) {
		printf("fail to create program");
		goto EXIT;
	}

	// 链接
	glAttachShader(program, vertexshader);
	glAttachShader(program, fragmentshader);
	glLinkProgram(program);

	// 查看链接结果
	GLint value = 0;
	glGetProgramiv(program, GL_LINK_STATUS, &value);
	if (value == GL_FALSE) {
		value = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value);
		if (value > 1) {
			GLchar info[value];
			glGetProgramInfoLog(program, value, NULL, info);
			printf("fail to link program: %s\n", info);
		}
		goto EXIT;
	}

	printf("link program successfully, delete no use shader %d %d\n", vertexshader, fragmentshader);
	glDetachShader(program, vertexshader);
	glDetachShader(program, fragmentshader);
	glDeleteShader(vertexshader);
	glDeleteShader(fragmentshader);
	return program;
EXIT:
	if (vertexshader != 0)
		glDeleteShader(vertexshader);
	if (fragmentshader != 0)
		glDeleteShader(fragmentshader);
	if (program != 0)
		glDeleteProgram(program);
	return 0;
}

int 
main() {
	if (!glfwInit()) 
		return 0;

#ifdef USE_OPENGL33
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
#endif
	
	GLFWwindow *window = glfwCreateWindow(800, 600, "draw triangle use modern opengl", NULL, NULL);
	if (window == NULL) {
		printf("fail to create window\n");
		glfwTerminate();
		return 0;
	}
	glfwMakeContextCurrent(window);

	glewExperimental = GL_TRUE;
	if (glewInit() != GLEW_OK) {
		glfwTerminate();
		return 0;
	}

	GLuint modern_program = link_program(vertexcode, fragmentcode);
	if (modern_program == 0) {
		glfwTerminate();
		return 0;
	}
	
	GLuint compat_program = link_program(vertexcode, fragmentcode);
	if (compat_program == 0) {
		glDeleteProgram(modern_program);
		glfwTerminate();
		return 0;
	}
	printf("modern_program:%d compat_program:%d\n", modern_program, compat_program);

	GLuint vao = 0;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
		glEnableVertexAttribArray(0); // 绑定 vao 后调用才对 vao 内的顶点属性有效果
		
		// left up
		const GLfloat vertices_leftup[] = {
			-1.0f, 0.0f, 0.0f,
			 0.0f, 0.0f, 0.0f,
			-0.5f, 1.0f, 0.0f,
		};
		GLuint leftup_bo = 0;
		glGenBuffers(1, &leftup_bo);
		glBindBuffer(GL_ARRAY_BUFFER, leftup_bo);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_leftup), vertices_leftup, GL_STATIC_DRAW);
		// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // 由于用 0 索引属性绘制两个三角形，就不需要在这里定义数组属性数据，要在绘制时定义，不然属性数据会被覆盖
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		
		// right up
		const GLfloat vertices_rightup[] = {
			0.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,
			0.5f, 1.0f, 0.0f,
		};
		GLuint rightup_bo = 0;
		glGenBuffers(1, &rightup_bo);
		glBindBuffer(GL_ARRAY_BUFFER, rightup_bo);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_rightup), vertices_rightup, GL_STATIC_DRAW);
		// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // 同上
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	
	// left down
	const GLfloat vertices_leftdown[] = {
		-1.0f, -1.0f, 0.0f,
		 0.0f, -1.0f, 0.0f,
		-0.5f,  0.0f, 0.0f,
	};
	GLuint leftdown_bo = 0;
	glGenBuffers(1, &leftdown_bo);
	glBindBuffer(GL_ARRAY_BUFFER, leftdown_bo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_leftdown), vertices_leftdown, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	
	// right down
	const GLfloat vertices_rightdown[] = {
		0.0f, -1.0f, 0.0f,
		1.0f, -1.0f, 0.0f,
		0.5f,  0.0f, 0.0f,
	};
	
	glClearColor(0.0f, 0.0f, 0.4f, 1.0f);

	while (!glfwWindowShouldClose(window)) {
		glClear(GL_COLOR_BUFFER_BIT);
		
		glUseProgram(modern_program);
		glBindVertexArray(vao);
			// draw left up
			glBindBuffer(GL_ARRAY_BUFFER, leftup_bo);
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // 在绘制时才定义数组属性数据
			glDrawArrays(GL_TRIANGLES, 0, 3);
			// draw right up
#ifdef VAO_WITH_BO
			glBindBuffer(GL_ARRAY_BUFFER, rightup_bo);
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
#else
			glBindBuffer(GL_ARRAY_BUFFER, 0); // 测试代码，取消注释着两行，看看不使用 buffer object 能不能成功绘制
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices_rightup);
#endif
			glDrawArrays(GL_TRIANGLES, 0, 3);
		glBindVertexArray(0);
		glUseProgram(0);
		
		glUseProgram(compat_program);
		// draw left down
		glBindBuffer(GL_ARRAY_BUFFER, leftdown_bo);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
		glDrawArrays(GL_TRIANGLES, 0, 3);
		glBindBuffer(GL_ARRAY_BUFFER, 0); // 由于下一个绘制没有用到缓冲区，这里要解绑缓冲区，不然会受到影响
		// draw right down
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices_rightdown);
		glDrawArrays(GL_TRIANGLES, 0, 3);
		glUseProgram(0);
		
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glDeleteProgram(modern_program);
	glDeleteVertexArrays(1, &vao);
	glDeleteBuffers(1, &leftup_bo);
	glDeleteBuffers(1, &rightup_bo);
	
	glDeleteProgram(compat_program);
	glDeleteBuffers(1, &leftdown_bo);
	
	glfwTerminate();
	return 0;
}

